/****************************************************************************
 *
 * Copyright (C) 2003-2008 Los Alamos National Security, LLC
 *                         Packet Analytics Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2 as
 * published by the Free Software Foundation.  You may not use, modify or
 * distribute this program under any other version of the GNU General
 * Public License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 ****************************************************************************/
package nfse.dataset.snort;

import nfse.NetFSE;
import nfse.NetFSERecord;
import nfse.NetFSEUtil;
import nfse.gui.record.FlexRecord;
import nfse.gui.record.FlexRecordField;
import nfse.gui.util.NetUtil;

import java.io.RandomAccessFile;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Vector;
import java.util.GregorianCalendar;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

public class SnortRecord extends NetFSERecord {

    public int sid = -1;

    public int cid = -1;

    public int sigClassID = -1;

    public int proto = -1;

    public String sigName = "";

    public int dstPort = -1;

    public int srcPort = -1;

    public long dstIP = -1;

    public String dstAddr = "";

    int priority = -1;

    String classification = "";

    String protoLine = "";

    String refLine = "";

    Vector<String> content = new Vector<String>();

    public SnortRecord() {

    }
    
    public static void main(String[] args) {
        SnortRecord rec = new SnortRecord();
        rec.parse(args, rec);
    }

    public void setDstIP(long dstIP) {
        this.dstIP = dstIP;
        this.dstAddr = NetFSEUtil.convertLongToIP(dstIP);
    }

    public int[] getIndexedFields() {
        int[] temp = { NetFSE.FIELD_START_TS, NetFSE.FIELD_DST_IP, NetFSE.FIELD_SRC_IP, NetFSE.FIELD_DST_PORT };
        return temp;
    }

    public String getRecordLine(RandomAccessFile dataIn) {
        try {
            StringBuffer temp = new StringBuffer();
            String line = dataIn.readLine();
            while (line != null && (line = line.trim()).length() > 0) {
                // System.out.println("SNORT: '" + line + "'");
                temp.append(line + "\n");
                line = dataIn.readLine();
            }
            // System.out.println(temp.toString());
            return temp.toString();
        } catch (Exception e) {

        }
        return null;
    }

    public String getRecordLine(GZIPInputStream dataIn) {
        try {
            StringBuffer line = new StringBuffer();
            int ch = 0;
            do {
                ch = dataIn.read();
                line.append((char) ch);

            } while ((ch != -1) && (line.indexOf("\n\n") == -1));
            // System.out.println("Zipped Line: " + line);
            return line.toString().substring(0, line.length() - 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public SnortRecord(int sensor) {
        this.setSensorID(sensor);
    }

    public FlexRecord getFlexRecord() {
        FlexRecord temp = new FlexRecord();
        temp.values = new String[11];
        int idx = -1;
        temp.values[++idx] = this.getTimestamp().toString();
        temp.values[++idx] = this.getSrcAddr();
        if (this.srcPort == -1)
            temp.values[++idx] = "";
        else
            temp.values[++idx] = this.srcPort + "";
        temp.values[++idx] = this.dstAddr;
        if (this.dstPort == -1)
            temp.values[++idx] = "";
        else
            temp.values[++idx] = this.dstPort + "";
        temp.values[++idx] = NetUtil.getProtocolName(proto) + "";
        temp.values[++idx] = this.sid + "";
        temp.values[++idx] = this.cid + "";
        temp.values[++idx] = this.priority + "";
        temp.values[++idx] = this.classification + "";
        temp.values[++idx] = this.sigName + "";
        return temp;
    }

    public FlexRecordField[] getFlexRecordFields() {
        FlexRecordField[] temp = new FlexRecordField[11];
        int idx = -1;
        temp[++idx] = new FlexRecordField("Start Time", "startts", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_TIMESTAMP);
        temp[++idx] = new FlexRecordField("Src IP", "srcip", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_IP_ADDR);
        temp[++idx] = new FlexRecordField("Src Port", "srcport", FlexRecordField.TYPE_INT,
                FlexRecordField.SUB_TYPE_PORT);
        temp[++idx] = new FlexRecordField("Dst IP", "dstip", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_IP_ADDR);
        temp[++idx] = new FlexRecordField("Dst Port", "dstport", FlexRecordField.TYPE_INT,
                FlexRecordField.SUB_TYPE_PORT);
        temp[++idx] = new FlexRecordField("Protocol", "protocol", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_NONE);
        temp[++idx] = new FlexRecordField("SID", "sid", FlexRecordField.TYPE_INT, FlexRecordField.SUB_TYPE_NONE);
        temp[++idx] = new FlexRecordField("CID", "cid", FlexRecordField.TYPE_INT, FlexRecordField.SUB_TYPE_NONE);
        temp[++idx] = new FlexRecordField("Priority", "priority", FlexRecordField.TYPE_INT,
                FlexRecordField.SUB_TYPE_NONE);
        temp[++idx] = new FlexRecordField("Classification", "classification", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_NONE);
        temp[++idx] = new FlexRecordField("Signature", "description", FlexRecordField.TYPE_STRING,
                FlexRecordField.SUB_TYPE_NONE);
        return temp;
    }

    public String toMultiLine() {
        StringBuffer temp = new StringBuffer("[**]");
        temp.append(" [" + this.sigClassID + ":" + cid + ":" + sid + "]");
        temp.append(" " + this.sigName + " [**]\n");
        if (classification.length() > 0 || priority != -1) {
            temp.append("[Classification: " + classification + "] [Priority: " + priority + "]\n");
        }
        temp.append(convertToSnortTS(getTimestamp()) + " " + this.getSrcAddr());
        if (srcPort != -1)
            temp.append(":" + srcPort);

        temp.append(" -> " + this.dstAddr);
        if (dstPort != -1)
            temp.append(":" + dstPort);
        temp.append("\n");

        if (protoLine.length() > 0)
            temp.append(protoLine + "\n");

        if (refLine.length() > 0) {
            temp.append("Len: " + refLine.length() + "\n");
            temp.append(refLine + "\n");
        }

        for (int i = 0; i < content.size(); i++)
            temp.append((String) content.elementAt(i) + "\n");

        return temp.toString();
    }

    public String getInsertFieldTableSQL(int id, String table) {
        StringBuffer temp = new StringBuffer("insert into " + table + " values (" + id + ",'");
        temp.append(this.getSrcAddr() + "',");
        temp.append(srcPort + ",'");
        temp.append(this.dstAddr + "',");
        temp.append(dstPort + ",");
        temp.append("" + this.proto + ",");
        temp.append(this.sid + ",");
        temp.append(this.cid + ",");
        temp.append(this.priority + ",");
        temp.append("'" + this.classification + "',");
        temp.append("'" + this.sigName + "',");
        temp.append("'" + this.getTimestamp() + "'");
        temp.append(")");
        return temp.toString();
    }

    public NetFSERecord generate(String line, int sensor) throws Exception {
        SnortRecord rec = new SnortRecord(sensor);
        // System.out.println(line);
        rec.setInitialString(line);
        rec.setTypeID(this.getTypeID());
        rec.setTypeName(this.getTypeName());
        line = line.trim();
        String[] lines = line.split("\n");

        for (int x = 0; x < lines.length; x++) {
            String[] lines2 = lines[x].split("\n");
            for (int i = 0; i < lines2.length; i++) {

                // System.out.println("line " + (i) + ": '" + lines[i] + "'");
                lines2[i] = lines2[i].trim();
                if (lines2[i].length() == 0) {
                    // ignore
                } else if (lines2[i].startsWith("[**")) {
                    Pattern p = Pattern.compile("\\[\\*\\*\\] \\[([0-9:]+)\\] (.*) \\[\\*\\*\\]");
                    Matcher m = p.matcher(lines2[0]);
                    boolean b = m.matches();
                    if (b) {
                        rec.sigName = m.group(2);
                        String value2 = m.group(1);
                        String[] sigValues = value2.split(":");
                        rec.sid = Integer.parseInt(sigValues[2]);
                        rec.cid = Integer.parseInt(sigValues[1]);
                        rec.sigClassID = Integer.parseInt(sigValues[0]);
                    } 
                } else if (lines2[i].startsWith("[Class")) {
                    Pattern p = Pattern.compile("\\[(.*)\\] \\[(.*)\\]");
                    Matcher m = p.matcher(lines2[i]);
                    boolean b = m.matches();
                    rec.classification = m.group(1).substring(16);
                    String value2 = m.group(2);
                    rec.priority = Integer.parseInt(value2.substring(10));
                } else if (lines2[i].startsWith("[Xref ")) {
                    rec.refLine = lines2[i];
                } else if (lines2[i].startsWith("TCP ") || lines2[i].startsWith("ICMP ")
                        || lines2[i].startsWith("UDP ")) {
                    String[] values = lines2[i].split(" ");
                    rec.proto = convertFromSnortProtocol(values[0]);
                } else {
                    try {
                        String[] values = lines2[i].split(" ");
                        rec.setTimestamp(convertFromSnortTS(values[0]));

                        int idx = 1;
                        if (values[idx].startsWith("{")) {
                            rec.proto = NetFSEUtil.getProtocol(values[idx].substring(1,values[idx].length()-1));
                            idx++;
                        }
                        
                        String srcIP = "";
                        int srcPort = -1;
                        String[] srcValues = values[idx].split(":");
                        if (srcValues.length <= 1) {
                            srcIP = values[idx];
                        } else {
                            srcIP = srcValues[0];
                            srcPort = Integer.parseInt(srcValues[1]);
                        }
                        rec.setSrcAddr(srcIP);
                        rec.srcPort = srcPort;
                        
                        idx += 2;

                        String dstIP = "";
                        int dstPort = -1;
                        String[] dstValues = values[idx].split(":");
                        if (dstValues.length <= 1) {
                            dstIP = values[idx];
                        } else {
                            dstIP = dstValues[0];
                            dstPort = Integer.parseInt(dstValues[1]);
                        }
                        rec.dstAddr = dstIP;
                        rec.dstPort = dstPort;

                        rec.setSrcIP(NetFSEUtil.convertIPtoLong(rec.getSrcAddr()));
                        rec.dstIP = NetFSEUtil.convertIPtoLong(rec.dstAddr);

                        // System.out.println(srcIP + "," + srcPort + "," +
                        // dstIP +
                        // "," +
                        // dstPort);

                    } catch (Exception e) {
                        rec.content.addElement(lines2[i]);
                        // System.out.println("---Bad line alert---");
                        // System.out.println(lines2[i]);
                        // e.printStackTrace();
                        // System.out.println(line.split("\n").length);
                    }
                }
            }
        }
        if (rec.getTimestamp() == null)
            return null;

        return rec;
    }

    private static Timestamp convertFromSnortTS(String snortTS) {
        // Example: 12/07/05-17:18:00.186017
        // System.out.println(snortTS);
        String[] values = snortTS.split("-");
        String[] dateValues = values[0].split("/");
        String[] timeValues = values[1].split(":");

        GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
        cal.set(Calendar.MONTH, Integer.parseInt(dateValues[0]) - 1);
        cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateValues[1]));
        if (dateValues.length == 3 && dateValues[2].length() == 2)
            cal.set(Calendar.YEAR, 2000 + Integer.parseInt(dateValues[2]));
        else if (dateValues.length == 3)
            cal.set(Calendar.YEAR, Integer.parseInt(dateValues[2]));
        cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeValues[0]));
        cal.set(Calendar.MINUTE, Integer.parseInt(timeValues[1]));
        cal.set(Calendar.SECOND, (int) Float.parseFloat(timeValues[2]));
        return new Timestamp(cal.getTimeInMillis());
    }

    private static String convertToSnortTS(Timestamp ts) {
        // Return Example: 12/07-17:18:00.186017

        GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance();
        cal.setTimeInMillis(ts.getTime());

        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        int year = cal.get(Calendar.YEAR);
        int hour = cal.get(Calendar.HOUR_OF_DAY);
        int minute = cal.get(Calendar.MINUTE);
        int second = cal.get(Calendar.SECOND);

        String temp = month + "/";
        if (day < 10)
            temp += "0";
        temp += day + "/";
        temp += year + "-";
        if (hour < 10)
            temp += "0";
        temp += hour + ":";
        if (minute < 10)
            temp += "0";
        temp += minute + ":";
        if (second < 10)
            temp += "0";
        temp += second + ".0";

        return temp;

    }

    private static int convertFromSnortProtocol(String protocol) {
        protocol = protocol.toUpperCase().trim();
        int proto = -1;
        if (protocol.equals("UDP")) {
            proto = 17;
        } else if (protocol.equals("TCP")) {
            proto = 6;
        } else if (protocol.equals("ICMP")) {
            proto = 1;
        } else {
            int idx = protocol.indexOf("PROTO");
            if (idx == 0) {
                proto = Integer.parseInt(protocol.substring(5));
            }
        }
        return proto;
    }

    public String getCreateFieldTableSQL(String table) {
        StringBuffer temp = new StringBuffer("create table " + table + " (ID bigint, ");
        temp.append("srcip inet,");
        temp.append("srcport int,");
        temp.append("dstip inet,");
        temp.append("dstport int,");
        temp.append("protocol int,");
        temp.append("sid int,");
        temp.append("cid int,");
        temp.append("priority int,");
        temp.append("classification varchar(255),");
        temp.append("description varchar(255),");
        temp.append("startts timestamp");
        temp.append(")");
        return temp.toString();
    }

    public long getDstIP() {
        return dstIP;
    }

    public int getDstPort() {
        return dstPort;
    }

    public int getSrcPort() {
        return srcPort;
    }
    
    public int getProtocol() {
        return proto;
    }
    
    public boolean isAlert() {
        return true;
    }
    
    public String getAlertMessage() {
        return this.sigName;
    }
    
    public String toXML() {
        StringBuffer temp = new StringBuffer();
        temp.append("<" + this.getTypeName() + " sensorid=\"" + getSensorID() + "\"");
        temp.append(" startts=\"" + this.getStartTS() + "\"");
        temp.append(" srcip=\"" + this.getSrcAddr() + "\"");
        temp.append(" dstip=\"" + this.getDstAddr() + "\"");
        temp.append(" srcport=\"" + this.getSrcPort() + "\"");
        temp.append(" dstport=\"" + this.getDstPort() + "\"");
        temp.append(" protocol=\"" + this.getProtocol() + "\""); 
        temp.append("/>");
        return temp.toString();
    }

}